/**
 *@file pilhas.c
 *@brief Biblioteca de ligação estática usada para
 * manipulação de pilhas.
 *
 *@details Pode manipular, pelo menos, SHRT_MAX + 1
 * pilhas.
 *\n
 *@code
 * #include <stdio.h>
 * #include <limits.h>
 *
 * int main(){
 *  printf("Pelo menos %u pilhas são suportadas\n",SHRT_MAX + 1);
 * }
 * @endcode
 * Cada pilha pode ter até UINT_MAX pratos.
 *@code
 * #include <stdio.h>
 * #include <limits.h>
 *
 * int main(){
 *  printf("Cada pilha pode ter até %u elementos\n",UINT_MAX);
 * @endcode
 * Exemplos dos limites\n
 * 32-bit \> 32,768 pilhas com 4,294,967,295 pratos, cada
 *
 */


static unsigned short int num_pilhas = 0;
static struct {
 Prato_t * pilha;
 Tamanho_t tamanho;
 Pilha_id id;
} * pilhas = NULL,aux,* err;


/**
 *@addtogroup pilhas
 *@{
 */

/**
 *@brief Função que você pode usar para
 * pegar o índice real de uma pilha.
 *
 *@param[in] id Número que identifica
 * a pilha.
 *
 *@return Retorna o índice da pilha no
 * no array mestre.
 *
 */
inline Pilha_id pegar_indice(Pilha_id id){
/**
 *@{
 */
 register int i;
 if(id<0)
  return -1;
 for(i=0;i<num_pilhas;++i){
  if(id == pilhas[i].id){
   return i;
  }
 }
 return -1;
/**
 *@}
 */
}

/**
 *@brief Função usada para descobrir o número de pilhas
 * alocadas.
 *@details
 *@code
 * printf("Há %hu pilhas alocadas\n",ver_num_pilhas());
 * @endcode
 *
 *@return Retorna o número de pilhas alocadas.
 *
 */
unsigned short int ver_num_pilhas( void ){
/**
 *@{
 */
 return num_pilhas;
/**
 *@}
 */
}

/**
 *@brief Cria uma pilha.
 *
 *@details Exemplo da criação de uma pilha.
 *@code
 * Pilha_id minha_pilha = nova_pilha();
 * if(minha_pilha < 0){
 *  fprintf(stderr,"Não foi possível criar uma pilha\n");
 * }
 * @endcode
 *
 *@return retorna -1 se nenhuma pilha foi criada
 * ou a ID (um número inteiro positivo) da pilha
 * se a operação foi bem sucedida.
 *
 *@see Pilha_id
 *
 */
Pilha_id nova_pilha( void ){
/**
 *@{
 */
 if(num_pilhas >= SHRT_MAX + 1){
  fprintf(stderr,"\nNúmero de pilhas esgotado!\n");
  return -1;
 }
 err = pilhas;
 pilhas = realloc(pilhas,sizeof(aux)*(++num_pilhas));
 if(pilhas == NULL){
  pilhas = err;
  --num_pilhas;
  return -1;
 }
 pilhas[num_pilhas-1].pilha = NULL;
 pilhas[num_pilhas-1].tamanho = 0;
 pilhas[num_pilhas-1].id = -1;
 {
  register unsigned short int i,j,num=0;
  register Bool_t nachou = TRUE;
  for(i=0;nachou;num=0){
   for(j=0;j<num_pilhas;++j){
    if(i == pilhas[j].id){
     ++i;
    }else{
     ++num;
    }
   }
   if(num == num_pilhas)
    nachou = FALSE;
  }
  pilhas[num_pilhas-1].id = i;
 }
 return pilhas[num_pilhas-1].id;
/**
 *@}
 */
}

/**
 *@details Exemplo de uso.
 *@code
 * if(!existe_pilha(minha_pilha)){
 *  minha_pilha = -1;
 *  printf("Pilha já foi destruída\n");
 * }else{
 *  printf("Pilha ainda existe\n");
 * }
 * @endcode
 *
 *@return Retorna TRUE se a pilha existe
 * e FALSE se a pilha não existe.
 *
 */
Bool_t existe_pilha(Pilha_id pilha){
/**
 *@{
 */
 Pilha_id indice = pegar_indice(pilha);
 if(indice == -1)
  return FALSE;
 else
  return TRUE;
/**
 *@}
 */
}

/**
 *@details Exemplo de uso
 *@code
 * if(esta_vazia(minha_pilha)){
 *  printf("Pilha está vazia ou não existe\n");
 * }
 * @endcode
 *
 *@warning Não use esta função com índices
 * de pilha inválidos ou para saber se a
 * pilha existe. No lugar, use existe_pilha
 * ou, antes de retornar FALSE indicando que,
 * nesse caso, a pilha não existe, será
 * enviado a stderr uma mensagem de erro
 * indicando que foi passado um índice de
 * pilha inválido.
 *
 */
Bool_t esta_vazia(Pilha_id pilha){
/**
 *@{
 */
 Pilha_id indice = pegar_indice(pilha);
 if(indice == -1){
  fprintf(stderr,"\nIndice de pilha invalido: %hi\n",pilha);
  return TRUE;
 }
 if(pilhas[indice].tamanho){
  return FALSE;
 }else{
  return TRUE;
 }
/**
 *@}
 */
}

/**
 *@return Retorna o tamanho de uma pilha
 *
 *@see STamanho_t
 *
 */
STamanho_t ver_tam_pilha(Pilha_id pilha){
/**
 *@{
 */
 Pilha_id indice = pegar_indice(pilha);
 if(indice == -1){
  return -1;
 }
 return pilhas[indice].tamanho;
/**
 *@}
 */
}

/**
 *@brief Função que empilha os pratos
 *
 *@details Você deve usar esta função para
 * colocar um \p elemento na \p pilha.
 * Exemplo.
 *\n
 *@code
 * Prato_t meu_prato;
 * if(colocar_na_pilha(minha_pilha,meu_prato)){
 *  printf("Novo prato colocado na pilha\n");
 * }
 * @endcode
 *
 *@return Retorna TRUE se o prato for
 * colocado na pilha e FALSE se não
 * conseguir empilhar o \p elemento na
 * pilha.
 *
 */
Bool_t colocar_na_pilha(Pilha_id pilha,Prato_t elemento){
/**
 *@{
 */
 Pilha_id indice = pegar_indice(pilha);
 if(indice == -1){
  fprintf(stderr,"\nIndice de pilha invalido: %hi\n",pilha);
  return FALSE;
 }
 if(pilhas[indice].tamanho >= UINT_MAX){
  fprintf(stderr,"\nTamanho da pilha esgotado!\n");
  return FALSE;
 }
 aux = pilhas[indice];
 pilhas[indice].pilha = realloc(pilhas[indice].pilha,sizeof(Prato_t)*(pilhas[indice].tamanho+1));
 if(pilhas[indice].pilha == NULL){
  pilhas[indice] = aux;
  fprintf(stderr,"\nFalha ao alocar memoria para novo prato na pilha %hi\n",pilha);
  return FALSE;
 }
 (pilhas[indice].pilha)[(pilhas[indice].tamanho)++] = elemento;
 return TRUE;
/**
 *@}
 */
}

/**
 *@brief Tira um prato da \p pilha
 *
 *@details Exemplo de código.
 *@code
 * Prato_t novo_prato = tirar_da_pilha(minha_pilha);
 * @endcode
 *
 *@return Retorna o prato retirado da
 * pilha ou lixo, caso a pilha esteja
 * vazia ou não existir.
 *
 */
Prato_t tirar_da_pilha(Pilha_id pilha){
/**
 *@{
 */
 Pilha_id indice = pegar_indice(pilha);
 if(indice == -1){
  fprintf(stderr,"\nIndice de pilha invalido: %hi\n",pilha);
  return;
 }
 if(esta_vazia(pilha)){
  fprintf(stderr,"\nPilha %hi esta vazia\n",pilha);
  return;
 }
 Prato_t prato = (pilhas[indice].pilha)[pilhas[indice].tamanho-1];
 aux = pilhas[indice];
 pilhas[indice].pilha = realloc(pilhas[indice].pilha,sizeof(Prato_t)*(--(pilhas[indice].tamanho)));
 if((pilhas[indice].pilha == NULL)&&(pilhas[indice].tamanho != 0)){
  pilhas[indice] = aux;
  fprintf(stderr,"\nFalha ao desalocar memoria do prato da pilha %hd\n",indice);
  ++(pilhas[indice].tamanho);
 }
 return prato;
/**
 *@}
 */
}

/**
 *@brief Vê um prato do topo da \p pilha
 *
 *@details Exemplo de código.
 *@code
 * Prato_t novo_prato = ver_na_pilha(minha_pilha);
 * @endcode
 *
 *@return Retorna o prato do topo da
 * pilha ou lixo, caso a pilha esteja
 * vazia ou não existir.
 *
 *@see tirar_da_pilha
 *
 */
Prato_t ver_na_pilha(Pilha_id pilha){
/**
 *@{
 */
 Pilha_id indice = pegar_indice(pilha);
 if(indice == -1){
  fprintf(stderr,"\nIndice de pilha invalido: %hi\n",pilha);
  return;
 }
 if(esta_vazia(pilha)){
  fprintf(stderr,"\nPilha %hi esta vazia\n",pilha);
  return;
 }
 return (pilhas[indice].pilha)[pilhas[indice].tamanho-1];
/**
 *@}
 */
}

/**
 *@brief Destrói \p pilha
 *
 *@return Retorna TRUE se a pilha for
 * destruída. Teoricamente o único
 * erro que pode ocorrer é você passar
 * um índice de pilha inválido.
 *
 *@see destruir_pilhas
 *
 */
Bool_t destruir_pilha(Pilha_id pilha){
/**
 *@{
 */
 Pilha_id indice = pegar_indice(pilha);
 if(indice == -1){
  fprintf(stderr,"\nIndice de pilha invalido: %hi\n",pilha);
  return FALSE;
 }
 if(num_pilhas==1){
  free(pilhas[0].pilha);
  free(pilhas);
  pilhas = NULL;
  num_pilhas = 0;
  return TRUE;
 }
 aux = pilhas[num_pilhas-1];
 err = pilhas;
 pilhas = realloc(pilhas,sizeof(aux)*(--num_pilhas));
 if(pilhas == NULL){
  pilhas = err;
  ++num_pilhas;
  fprintf(stderr,"Falha 0, ao desalocar pilha\n");
  return FALSE;
 }
 if(indice == num_pilhas-1){
  free(aux.pilha);
  return TRUE;
 }else{
  free(pilhas[indice].pilha);
  pilhas[indice] = aux;
  return TRUE;
 }
/**
 *@}
 */
}

/**
 *@brief Destrói todas as pilhas
 *
 *@return Retorna TRUE se a operação for
 * bem-sucedida. Se não há nenhuma pilha
 * ela imprime uma mensagem de erro em
 * stderr e retorna TRUE.
 *
 *@see destruir_pilha
 *
 */
Bool_t destruir_pilhas( void ){
/**
 *@{
 */
 if(num_pilhas == 0){
  return TRUE;
 }
 {
  register unsigned short int i;
  for(i=0;i<num_pilhas;++i){
   free(pilhas[i].pilha);
  }
 }
 free(pilhas);
 pilhas = NULL;
 num_pilhas = 0;
 return TRUE;
/**
 *@}
 */
}

/**
 *@}
 */
